package org.lds.logger.impl;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.lds.logger.ILoggerFactory;
import org.lds.logger.Logger;
import org.lds.logger.NOPLogger;
import org.lds.logger.config.Configuration;
import org.lds.logger.config.level.Level;
import org.lds.logger.xml.ConfigurationXMLReader;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Data
@AllArgsConstructor
@Builder
public class LoggerFactoryImpl implements ILoggerFactory {
    private Configuration configuration;
    private LoggerImpl rootLogger;
    private Map<String, Logger> loggerCache = new ConcurrentHashMap<>();

    public LoggerFactoryImpl() {
        bindConfiguration();

        createRootLogger();
    }

    private void bindConfiguration() {
        ConfigurationXMLReader xmlReader = new ConfigurationXMLReader();
        configuration = xmlReader.parse();
    }

    private void createRootLogger() {
        rootLogger = new LoggerImpl("root", configuration.getRootLevel(), configuration);
    }

    @Override
    public Logger getLogger(String name) {
        if (configuration == null || StringUtils.isEmpty(name)) return new NOPLogger();

        String rootLoggerName = rootLogger.getName();
        if (rootLoggerName.equals(name)) return rootLogger;

        Logger logger = loggerCache.get(name);
        if (logger != null) return logger;

        return getLoggerByTree(name);
    }

    private Logger getLoggerByTree(String name) {
        String[] loggerNameSplits = name.split("\\.");
        if (loggerNameSplits == null || loggerNameSplits.length == 0) return rootLogger;

        LoggerImpl parentLogger = rootLogger;
        StringBuilder loggerNameBuilder = new StringBuilder();
        for (String loggerNameSplit : loggerNameSplits) {
            if (loggerNameBuilder.length() > 0) {
                loggerNameBuilder.append(".");
            }
            loggerNameBuilder.append(loggerNameSplit);

            String currLoggerName = currLoggerName(loggerNameBuilder);
            Logger currLogger = loggerCache.get(currLoggerName);
            if (currLogger == null) {
                currLogger = new LoggerImpl(currLoggerName, getLevel(currLoggerName, parentLogger), configuration);
                loggerCache.put(currLoggerName, currLogger);
            }

            parentLogger = (LoggerImpl) currLogger;
        }

        return parentLogger;
    }

    private Level getLevel(String name, LoggerImpl parentLogger) {
        Map<String, Level> loggerLevelMap = configuration.getLoggerLevelMap();
        Level level = loggerLevelMap.get(name);
        if (level != null) return level;

        return parentLogger.getLevel();
    }

    private String currLoggerName(StringBuilder loggerNameBuilder) {
        return loggerNameBuilder.toString();
    }

}
